package com.huida.app.config;

import com.huida.app.handler.MyAuthenticationFailureHandler;
import com.huida.app.handler.MyAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;

/**
 * @Description:
 * @Author: Wangzz
 * @Version: 1.0.0
 * @CreateDate: Created in 2021/10/31 21:32
 * @UpdateDate: [dateFormat:YYYY-MM-DD][modifier][description]
 */

@Configuration
public class SecurityConfig  extends WebSecurityConfigurerAdapter {

//    @Override
//    protected void configure(HttpSecurity httpSecurity) throws Exception {
//        httpSecurity.httpBasic() // 开启httpBasic认证
//                    .and()
//                    .authorizeRequests()
//                    .anyRequest()
//                    .authenticated(); // 所有请求都需要登录认证才能访问
//    }

    // 第四步： 配置自定义结果处理
    @Resource
    private MyAuthenticationSuccessHandler successHandler;

    @Resource
    private MyAuthenticationFailureHandler failureHandler;

    // 第一步: 登录认证及资源访问权限的控制
    @Override
    protected void configure(HttpSecurity security) throws Exception {
        security.csrf().disable() // 禁用跨站csrf攻击防御

                /*
                第一部分是formLogin配置段，用于配置登录验证逻辑相关的信息。
                如：登录页面、登录成功页面、登录请求处理路径等。和login.html页面的元素配置要一一对应。
                * */
                .formLogin()

                    .loginPage("/login.html") // 一旦用户的请求没有权限就跳转到这个页面
                    .loginProcessingUrl("/login") // 登录表单中form中action的地址，是处理认证请求的路径
                    .usernameParameter("username") // 登录表单时用户名输入框input的name名默认是username
                    .passwordParameter("password") // form中密码输入框input的name名，不修改的话默认是password
                    .successHandler(successHandler)
                    .failureHandler(failureHandler)
                    /*
                    *将自定义的AuthenticationSuccessHandler和AuthenticationFailureHandler注入到Spring Security配置类中
                    *使用fromlogin模式，配置successHandler和failureHandler。
                    *不要配置defaultSuccessUrl和failureUrl，否则自定义handler将失效。handler配置与URL配置只能二选一
                    * */
                    //.defaultSuccessUrl("/") // 登录认证成功后默认跳转的路径
                    //.failureUlr("/login.html") // 登录认证失败后默认跳转的路径
                .and()
                /*
                * 第二部分是authorizeRequests配置段，用于配置资源的访问控制规则。
                * 如：开发登录页面的permitAll开放访问，“/biz1”（业务一页面资源）需要有角色为user或admin的用户才可以访问。
                * */
                    .authorizeRequests()
                    .antMatchers("/login.html", "/login").permitAll() // 不需要通过登录验证就可以访问的资源路径
                    .antMatchers("/", "/biz1", "biz2") // 资源路径匹配
                        .hasAnyAuthority("ROLE_user", "ROLE_admin") // user角色和admin角色都可以访问
                    .antMatchers("/syslog","/sysuser") // 资源路径匹配
                        .hasAnyRole("admin") // admin角色可以访问
                /*
                * hasAnyAuthority("ROLE_user","ROLE_admin")
                * 等价于hasAnyRole("user","admin"),角色是一种特殊的权限
                *
                * "sys:log"或"sys:user"是我们自定义的权限ID，有这个ID的用户可以访问对应的资源
                * */
                    // .antMatchers("/syslog").hasAuthority("sys:log")
                    // .antMatchers("/sysuser").hasAuthor("sys:user")
                    .anyRequest().authenticated()
                .and()
                    // 配置session会话过期管理
                    .sessionManagement()
                    .sessionFixation().migrateSession() // session-fixation-protection是session固化保护功能
                    .sessionCreationPolicy( // session生成策略
                            SessionCreationPolicy.IF_REQUIRED
                    )
                    .invalidSessionUrl("/invalidSession.html"); // 非法超时session跳转页面

    }

    // 第二步：用户及角色信息配置
    /*
    * 在上文中，我们配置了登录验证及资源访问的权限规则，
    * 我们还没有具体的用户，下面我们就来配置具体的用户。重写WebSecurityConfigurerAdapter的 configure(AuthenticationManagerBuilder auth)方法
    * */

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication() // inMemoryAuthentication指的是在内存里面存储用户的身份认证和授权信息。
                .withUser("user") // withUser("user")用户名是user
                .password(passwordEncoder().encode("123456")) // password(passwordEncoder().encode("123456"))密码是加密之后的123456
                .roles("user")
            .and()
                .withUser("admin")
                .password(passwordEncoder().encode("123456"))
                //.authorities("sys:log","sys:user") // authorities("sys:log","sys:user")指的是admin用户拥有资源ID对应的资源访问的的权限："/syslog"和"/sysuser"
                .roles("admin") // roles()方法用于指定用户的角色，一个用户可以有多个角色
            .and()
                .passwordEncoder(passwordEncoder()); // 配置BCrypt加密

    }

    // 第三步： 静态资源访问
    /*
    * 登录页面login.html和控制层Controller登录验证'/login'都必须无条件的开放。
    * 除此之外，一些静态资源如css、js文件通常也都不需要验证权限，我们需要将它们的访问权限也开放出来。
    * 下面就是实现的方法：重写WebSecurityConfigurerAdapter类的configure(WebSecurity web) 方法
    * */

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 将项目中静态资源路径开发出来
        web.ignoring().antMatchers("/css/**", "/fonts/**", "/img/**", "/js/**");
    }
    /*
    * 静态资源的开放，和Controller服务资源的开放为什么要分开配置？有什么区别呢？
        Controller服务资源要经过一系列的过滤器的验证，我们配置的是验证的放行规则
        这里配置的是静态资源的开放，不经过任何的过滤器链验证，直接访问资源。
    * */


}



/*
{
isok: true,
code: 200,
message: "请求响应成功！",
data: null
}
* */